package vn.edu.hut.soict.translate.transform;

import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import vn.edu.hut.soict.alignment.AlignmentParser;
import vn.edu.hut.soict.alignment.BiDirectionalSyllableAlignment;
import vn.edu.hut.soict.alignment.BiDirectionalTokenAlignment;
import vn.edu.hut.soict.lienkate.dictionary.Dictionary;
import vn.edu.hut.soict.lienkate.dictionary.DictionaryReader;
import vn.edu.hut.soict.lienkate.dictionary.VietDictionaryReader;
import vn.edu.hut.soict.lienkate.parser.Linkage;
import vn.edu.hut.soict.lienkate.parser.NewLinkageDrawer;
import vn.edu.hut.soict.lienkate.parser.exprprune.ExpressionPruneParser;
import vn.edu.hut.soict.translate.database.TranslatorDatabase;
import vn.edu.hut.soict.translate.dictionary.bilingual.BilingualDictionary;
import vn.edu.hut.soict.translate.dictionary.bilingual.BilingualDictionaryReader;
import vn.edu.hut.soict.translate.dictionary.bilingual.BilingualDictionaryWithPOS;
import vn.edu.hut.soict.translate.dictionary.bilingual.BilingualDictionaryWithPOSReader;
import vn.edu.hut.soict.translate.dictionary.bilingual.PartOfSpeech;
import vn.edu.hut.soict.translate.search.Operator;
import vn.edu.hut.soict.translate.search.Record;
import vn.edu.hut.soict.translate.transform.utils.GrammaticalConstant;
import vn.edu.hut.soict.translate.transform.utils.GrammaticalNumber;
import vn.edu.hut.soict.translate.transform.utils.NounTransformUtils;
import vn.edu.hut.soict.translate.transform.utils.VerbTransformUtils;
import vn.edu.hut.soict.translate.utils.StringUtils;
import vn.edu.hut.soict.translate.wordgraph.Word;

public class OldTransformer {
	private static BilingualDictionaryWithPOS bilingualDictionary;
	private static Dictionary enDictionary;
	private static Dictionary viDictionary;

	private ExpressionPruneParser enParser;
	private ExpressionPruneParser viParser;
	private TranslatorDatabase database;

	List<String> enTokenList;
	List<String> viTokenList;
	List<String> inputTokenList;
	private String enSentence;
	private String viSentence;
	Linkage enLinkage;
	Linkage viLinkage;
	Linkage inputLinkage;

	private List<Record> source;
	// private List<String> destination;
	BiDirectionalTokenAlignment alignment;

	/*
	 * Map a Vietnamese token to its corresponding records.
	 */
	private Map<Integer, Record> tokenToRecordMap;

	/*
	 * Map an English phrase (a.k.a. a group of tokens) to their corresponding Vietnamese phrase.
	 */
	private Map<List<Integer>, List<Integer>> phraseMap;

	private String graphWord;
	private String inputWord;
	private Record currentRecord;
	
	private SubstitutionTransformer sTransformer;
	private DeletionTransformer dTransformer;
	private InsertionTransformer iTransformer;
	

	static {
		DictionaryReader enReader = new DictionaryReader();
		VietDictionaryReader viReader = new VietDictionaryReader();
		try {
			enDictionary = enReader.read("res/dictionary/lg-en/4.0.dict");
			viDictionary = viReader.read("res/dictionary/lg-vi-new/dictionary.txt");
		}
		catch (IOException e) {
			e.printStackTrace();
		}
		catch (ParseException e) {
			e.printStackTrace();
		}

		BilingualDictionaryWithPOSReader reader = new BilingualDictionaryWithPOSReader();
		bilingualDictionary = reader.read(BilingualDictionaryWithPOS.DICTIONARY_FILE);
	}

	public OldTransformer() {
		enParser = new ExpressionPruneParser(enDictionary);
		viParser = new ExpressionPruneParser(viDictionary);
		database = new TranslatorDatabase();
		database.connect();
	}
	
	public String getVietnameseSentence() {
		return viSentence;
	}

	public String getEnglishSentence() {
		return enSentence;
	}

	private void init(List<Record> source) {
		this.source = source;
		initSentences();
		initLinkages();
		initMapping();
		// destination = new ArrayList<String>(enTokenList);
		initTransformer();
	}

	private void initSentences() {
		StringBuilder sb = new StringBuilder();
		viTokenList = new ArrayList<String>();
		inputTokenList = new ArrayList<String>();
		for (Record record : source) {
			Word graphWord = record.getGraphWord();
			Word inputWord = record.getInputWord();
			if (graphWord != null) {
				String text = graphWord.getText();
				sb.append(text).append(" ");
				viTokenList.add(text);
			}
			if (inputWord != null) {
				inputTokenList.add(inputWord.getText());
			}
		}
		viSentence = sb.toString().trim();
		System.out.println(viSentence);
		System.out.println(source);
		enSentence = database.getEnglishSentence(viSentence);
		alignment = new BiDirectionalTokenAlignment(viSentence, enSentence, database.getAlignment(viSentence));
		enTokenList = alignment.getDestination();
	}

	private void initMapping() {
		tokenToRecordMap = new HashMap<Integer, Record>();
		int index = 0;
		for (Record record : source) {
			if (record.getOperator() != Operator.INSERTION) {
				tokenToRecordMap.put(index++, record);
			}
		}

		phraseMap = new HashMap<List<Integer>, List<Integer>>();
		for (int i = 0; i < enTokenList.size(); i++) {
			List<Integer> values = alignment.sourceIndexesAlignWith(i);
			Iterator<List<Integer>> iterator = phraseMap.keySet().iterator();
			if (phraseMap.containsValue(values)) {
				while (iterator.hasNext()) {
					List<Integer> key = iterator.next();
					if (values == null ? phraseMap.get(key) == null
							: phraseMap.get(key).equals(values)) {
						iterator.remove();
						key.add(i);
						phraseMap.put(key, values);
						break;
					}
				}
			}
			else {
				List<Integer> key = new ArrayList<Integer>();
				key.add(i);
				phraseMap.put(key, values);
			}
		}
	}

	private void initLinkages() {
		enLinkage = null;
		viLinkage = null;
		try {
			List<Linkage> enLinkages = enParser.parse(enTokenList, 0);
			System.out.println(NewLinkageDrawer.toString(enLinkages, enTokenList));
			List<Linkage> viLinkages = viParser.parse(viTokenList, 0);
			System.out.println(NewLinkageDrawer.toString(viLinkages, viTokenList));
			List<Linkage> inputLinkages = viParser.parse(inputTokenList, 0);
			System.out.println(NewLinkageDrawer.toString(inputLinkages, inputTokenList));
			enLinkage = enLinkages.get(0);
			viLinkage = viLinkages.get(0);
			inputLinkage = inputLinkages.get(0);
		}
		catch (ParseException e) {
//			e.printStackTrace();
			System.err.println(e.getMessage());
		}
	}
	
	private void initTransformer() {
//		sTransformer = new SubstitutionTransformer(this);
//		dTransformer = new DeletionTransformer(this);
	}

	private void reParseEnLinkages() {
		enLinkage = null;
		try {
			List<Linkage> enLinkages = enParser.parse(enTokenList, 0);
			System.out.println(NewLinkageDrawer.toString(enLinkages, enTokenList));
			enLinkage = enLinkages.get(0);
		}
		catch (ParseException e) {
			e.printStackTrace();
		}
	}

	public String transform(List<Record> source) {
		if (source == null) {
			return null;
		}
		
		init(source);

		if (enSentence == null) {
			System.err.println("Database error: Cannot find corresponding english sentence.");
			return null;
		}

		for (Entry<List<Integer>, List<Integer>> entry : phraseMap.entrySet()) {
			List<Integer> key = entry.getKey();
			List<Integer> value = entry.getValue();
			List<Record> records = new ArrayList<Record>();

			for (Integer i : value) {
				if (i != null) {
					Record record = tokenToRecordMap.get(i);
					if (!(records.contains(record))) {
						records.add(record);
					}
				}
			}

			if (key.size() == 1) {
				int index = key.get(0);
				if (records.size() == 0) {
					
				}
				else if (records.size() == 1) {
					Record record = tokenToRecordMap.get(value.get(0));
					if (record != null) {
						switch (record.getOperator()) {
							case EXACT_MATCH:
								transformExtractMatch(index, record);
								break;
							case SUBSTITUTION:
								transformSubsitution(index, record);
								break;
							case DELETION:
								//transformDeletion(index, record);
								break;
						}
					}
				}
				else if (records.size() == 2) {
					Record firstRecord = records.get(0);
					Record secondRecord = records.get(1);
					if (firstRecord.getGraphWord().partOfSpeech().equals("R") && secondRecord.getGraphWord().partOfSpeech().equals("V")) {
						/* Trường hợp phó từ - động từ.
						 * Phó từ chỉ có thể là "đã" hoặc "đang".
						 * TODO
						 */
						String verb;

//						switch (firstRecord.getOperator()) {
//							case DELETION:
//								verb = VerbTransformUtils.conjugate(enTokenList.get(index), GrammaticalConstant.BARE);
//								enTokenList.set(index, verb);
//								break;
//						}

						switch (secondRecord.getOperator()) {
							case EXACT_MATCH:
								transformExtractMatch(index, secondRecord);
								break;
							case SUBSTITUTION:
								transformSubsitution(index, secondRecord);
								break;
							case DELETION:
								//transformDeletion(index, secondRecord);
								break;
						}
					}
					else if (firstRecord.getGraphWord().partOfSpeech().equals("L") && secondRecord.getGraphWord().partOfSpeech().equals("N")) {
						/* Trường hợp định từ - danh từ.
						 * Định từ chỉ có thể là "các","những" hoặc "cái".
						 */
//						switch (firstRecord.getOperator()) {
//							case DELETION:
//								String noun = GrammaticalNumber.nounDecline(enTokenList.get(index), GrammaticalConstant.SINGULAR);
//								enTokenList.set(index, noun);
//								break;
//						}

						switch (secondRecord.getOperator()) {
							case EXACT_MATCH:
								transformExtractMatch(index, secondRecord);
								break;
							case SUBSTITUTION:
								transformSubsitution(index, secondRecord);
								break;
							case DELETION:
								//transformDeletion(index, secondRecord);
								break;
						}
					}
					else if (firstRecord.getGraphWord().partOfSpeech().equals("N") && secondRecord.getGraphWord().partOfSpeech().equals("P")) {
						/*
						 * Trường hợp đại từ bị phân tích thành danh từ - đại từ hoặc sở hữu.
						 * VD: cô ấy, anh ấy, của tôi, của họ.
						 */

					}
				}
				else if (records.size() == 3) {
					/*
					 * Trường hợp sở hữu.
					 * VD: của chúng tôi, của anh ấy, của cô ấy...
					 */
				}
				else {
					// TODO: Chưa nghĩ ra trường hợp nào ở đây.
					System.err.println("I never think of being here");
				}
			}
			else {
				System.err.println("Not supported yet");
			}
		}

		// List<Integer> viSyllableIndexes =
		// alignmentParser.sourceIndexesAlignWith(i);
		//
		// currentRecord = null;
		// if (viSyllableIndexes != null) {
		// for (Integer index : viSyllableIndexes) {
		// if (index != null) {
		// Record record = tokenRecordMap.get(index);
		// if (currentRecord == null) {
		// currentRecord = record;
		// }
		// else {
		// if (currentRecord != record) {
		// System.err.printf("English token %d: Different records are binded with one English token.\n",
		// i);
		// return null;
		// }
		// }
		// }
		// }
		// }

		// graphWord = (record.getGraphWord() != null ?
		// record.getGraphWord().getText() : null);
		// inputWord = (record.getInputWord() != null ?
		// record.getInputWord().getText() : null);
		//
		// if (currentRecord != null) {
		// switch (currentRecord.getOperator()) {
		// case EXACT_MATCH:
		// transformExtractMatch(i);
		// break;
		// case SUBSTITUTION:
		// transformSubsitution(i);
		// break;
		// case DELETION:
		// transformDeletion(i);
		// break;
		// }
		// }
		// }

		//sTransformer.fixDeterminer();
		transformDeletion();
		transformInsertion();

		StringBuilder sb = new StringBuilder();
		for (String s : enTokenList) {
			if (s != null) {
				sb.append(s).append(" ");
			}
		}
		if (sb.length() > 0) {
			sb.deleteCharAt(sb.length() - 1);
		}

		return sb.toString();
	}

	private void transformExtractMatch(int i, Record record) {

	}

	private void transformSubsitution(int i, Record record) {
		String viWord = record.getInputWord().getText();
		PartOfSpeech pos = PartOfSpeech.valueOf(record.getInputWord().partOfSpeech());

		List<String> meanings = bilingualDictionary.meaning(viWord, pos);
		String bestMeaning = enTokenList.get(i);
		if (meanings != null) {
			bestMeaning = chooseBestMeaning(meanings);
		}

		// String oldEnWord = destination.get(i);
		// switch (pos){
		// case N:
		// //bestMeaning = NounTransformUtils.conjugate(bestMeaning,
		// NounTransformUtils.getForm(oldEnWord));
		// break;
		// case V:
		// //bestMeaning = VerbTransformUtils.conjugate(bestMeaning,
		// VerbTransformUtils.getForm(oldEnWord));
		// break;
		// }

		// destination.set(i, bestMeaning);
		sTransformer.substitute(i, bestMeaning);
	}

	private void transformInsertion() {
		int index = -1;
		for (Record record : source) {
//			if (record.getOperator() != Operator.INSERTION) {
//				index++;
//			}
//			else {
//				List<Integer> insertIndexList = phraseMap.get(Collections.singletonList(index));
//				int insertIndex = insertIndexList.get(insertIndexList.size() - 1);
//				List<String> meanings = bilingualDictionary.meaning(record.getInputWord().getText(), PartOfSpeech.valueOf(record.getInputWord().partOfSpeech()));
//				String bestMeaning = chooseBestMeaning(meanings);
//				insertAfter(insertIndex, bestMeaning);
//				index++;
				// case SUBSTITUTION:
				// String tenseWord = firstRecord.getInputWord().getText();
				// if (tenseWord.equalsIgnoreCase("đã")) {
				// verb = VerbTransformUtils.conjugate(enTokenList.get(index),
				// GrammaticalConstant.PAST_SIMPLE);
				// enTokenList.set(index, verb);
				// }
				// else if (tenseWord.equalsIgnoreCase("đang")) {
				// verb = VerbTransformUtils.conjugate(enTokenList.get(index),
				// GrammaticalConstant.PRESENT_PARTICIPLE);
				// enTokenList.set(index, verb);
				// }
				// else {
				// List<String> meanings =
				// bilingualDictionary.meaning(tenseWord,
				// PartOfSpeech.valueOf("R"));
				// String bestMeaning = chooseBestMeaning(meanings);
				// if (bestMeaning != null) {
				// // Thêm phó từ mới vào trước động từ
				// enTokenList.set(index, bestMeaning);
				// reParseEnLinkages();
				// index++;
				// }
				// }
//			}
		}
	}

	private void insertAfter(int index, String meaning) {
		if (index < enTokenList.size() - 1) {
			enTokenList.add(index + 1, meaning);
		}
		else {
			enTokenList.add(meaning);
		}
	}

	private void insertBefore(int index, String meaning) {
		enTokenList.add(index, meaning);
	}

	private void transformDeletion() {
		for (int i = 0; i < viTokenList.size(); i++) {
			Record record = tokenToRecordMap.get(i);
			if (record.getOperator() == Operator.DELETION) {
				dTransformer.delete(i);
			}
		}
	}

	private String chooseBestMeaning(List<String> meanings) {
		if (meanings != null && meanings.size() > 0) {
			return meanings.get(0);
		}
		return null;
	}

}
